﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Web;
using com.eforcegloabal.crux.web.wrokflows;
using com.eforceglobal.crux.configuration;
using com.eforceglobal.crux.web.wrokflows.DTOs;

namespace com.eforceglobal.crux.web.wrokflows
{
    public static class ConfigParser
    {
        private static XDocument LoadConfigurationDocument(string WorkflowName)
        {
            XDocument xDocument = (XDocument)HttpRuntime.Cache.Get(WorkflowName + "_workflow");
            if (xDocument == null)
            {
                string siteConfigurationFolder = CruxConfigurationManager.GetConfigurationFolderName();
                string workflowFolder = CruxConfigurationManager.GetWorkflowFolderName();
                string fileName = HttpContext.Current.Server.MapPath("~/" + siteConfigurationFolder + "/" + workflowFolder + "/" + WorkflowName + ".xml");
                xDocument = XDocument.Load(fileName);
                HttpRuntime.Cache.Add(WorkflowName + "_workflow", xDocument, new System.Web.Caching.CacheDependency(fileName), DateTime.Now.AddDays(7),
                TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, null);
            }
            return xDocument;
        }

        
        public static Workflow GetWorkflowConfiguration(string WorkflowName)
        {
            Workflow workflow = new Workflow();
            XElement workflowElement = GetWorkflowElement(WorkflowName);
            workflow.WorkflowName = workflowElement.Attribute("Name").Value;
            workflow.WorkflowNotificationsOn = bool.Parse(workflowElement.Attribute("EmailNotificationsOnStateChange").Value);
            workflow.WorkflowType = workflowElement.Attribute("Type").Value;
            workflow.Activites = GetActivitiesForWorflow(WorkflowName);
            string StartAcitivityName = workflowElement.Attribute("StartActivity").Value;
            string EndAcitivityName = workflowElement.Attribute("EndActivity").Value;
            workflow.StartActivity = GetActivity(WorkflowName, StartAcitivityName);
            if (!string.IsNullOrEmpty(EndAcitivityName))
            {
                workflow.EndActivity = GetActivity(WorkflowName, EndAcitivityName);
            }
            else
            {
                workflow.EndActivity = null;
            }
            return workflow;
        }

        public static Action GetAction(string WorkflowName, string ActivityName, string ActionName)
        {
            XElement activityConfiguration = GetActivityElement(WorkflowName, ActivityName);
            XElement actionElement = activityConfiguration.Element("Actions")
                                                            .Elements("Action")
                                                            .Single(p => p.Attribute("Name").Value == ActionName);
            Action action = new Action();
            action.ActionName = actionElement.Attribute("Name").Value;

            if (actionElement.Attribute("ResultantActivity") != null)
                action.ResultantAcitivity = actionElement.Attribute("ResultantActivity").Value;

            if (actionElement.Attribute("ActionClass") != null)
            {
                string[] DllClassName = actionElement.Attribute("ActionClass").Value.Split(',');
                if (!string.IsNullOrEmpty(DllClassName[0].Trim()))
                {
                    string ClassName = DllClassName[0].Trim();
                    string DllName = DllClassName[1].Trim();
                    action.ActionDll = DllName;
                    action.ActionClass = ClassName;
                }
            }
            if (actionElement.Attribute("RoutingRule") != null)
            {
                action.RoutingRuleName = string.IsNullOrEmpty(actionElement.Attribute("RoutingRule").Value) ? string.Empty : actionElement.Attribute("RoutingRule").Value;
            }
            return action;
        }

        public static Activity GetActivity(string WorkFlowName, string ActivityName)
        {
            XElement activityConfiguration = GetActivityElement(WorkFlowName, ActivityName);
            Activity activity = GetAcitvityFromConfiguration(activityConfiguration);
            activity.Actions = GetActionsForActivity(WorkFlowName, ActivityName);
            activity.EmailAction = GetEmailActionForActivity(WorkFlowName, activity.State);
            return activity;
        }

        
        private static List<Activity> GetActivitiesForWorflow(string WorkFlowName)
        {
            XElement workflowElement = GetWorkflowElement(WorkFlowName);
            IEnumerable<XElement> activityDetails = workflowElement.Element("Activities").Elements("Activity");
            List<Activity> activities = new List<Activity>();
            foreach (XElement activityConfiguration in activityDetails)
            {
                Activity activity = GetActivity(WorkFlowName, activityConfiguration.Attribute("Name").Value);
                activities.Add(activity);
            }
            return activities;
        }

        private static XDocument LoadConfigurationDocument()
        {

            XDocument xDocument = (XDocument)HttpRuntime.Cache.Get("WorkflowConfigurationFile");
            if (xDocument == null)
            {
                string siteConfigurationFolder = CruxConfigurationManager.GetConfigurationFolderName();
                string fileName = HttpContext.Current.Server.MapPath("~/" + siteConfigurationFolder + "/workflows.xml");
                xDocument = XDocument.Load(fileName);
                HttpRuntime.Cache.Add("WorkflowConfigurationFile", xDocument, new System.Web.Caching.CacheDependency(fileName), DateTime.Now.AddDays(7),
                TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, null);
            }
            return xDocument;
        }

        public static List<Action> GetActionsForActivity(string WorkFlowName, string ActivityName)
        {
            XElement activityConfiguration = GetActivityElement(WorkFlowName, ActivityName);
            XElement actionsElements = activityConfiguration.Element("Actions");
            IEnumerable<XElement> activityActionsConfiguration = new List<XElement>();
            if (actionsElements != null)
                activityActionsConfiguration = actionsElements.Elements("Action");

            List<Action> actions = new List<Action>();

            foreach (XElement actionConfiguration in activityActionsConfiguration)
            {
                Action action = GetAction(WorkFlowName, ActivityName, actionConfiguration.Attribute("Name").Value);
                actions.Add(action);
            }
            return actions;
        }

        public static string GetEmailActionForActivity(string WorkflowName, string StateName)
        {
            XElement activityConfiguration = GetActivityElementFromState(WorkflowName, StateName);
            if(activityConfiguration != null && activityConfiguration.Element("MailAction") != null)
               return  activityConfiguration.Element("MailAction").Attribute("Name").Value;
            return "";
        }


        private static XElement GetActivityElement(string WorkflowName, string ActivityName)
        {
            XElement workflow = GetWorkflowElement(WorkflowName);
            XElement activityConfiguration = workflow.Element("Activities")
                                                     .Elements("Activity")
                                                     .SingleOrDefault(p => p.Attribute("Name").Value == ActivityName);
            if (activityConfiguration == null) throw CruxWorkflowExceptions.AcitivityNotFoundInWorkflowException;
            return activityConfiguration;

        }

        private static XElement GetActivityElementFromState(string WorkflowName, string StateName)
        {
            XElement workflow = GetWorkflowElement(WorkflowName);
            XElement activityConfiguration = workflow.Element("Activities")
                                                     .Elements("Activity")
                                                     .SingleOrDefault(p => p.Attribute("State").Value == StateName);
            if (activityConfiguration == null) throw CruxWorkflowExceptions.AcitivityNotFoundInWorkflowException;
            return activityConfiguration;

        }


        private static Activity GetAcitvityFromConfiguration(XElement AcivitiyConfigurationElement)
        {
            string ActivityName = AcivitiyConfigurationElement.Attribute("Name").Value;
            string WorkflowName = AcivitiyConfigurationElement.Parent.Parent.Attribute("Name").Value;
            string DefaultInstanceName = "";
            Activity activity = new Activity();
            if (AcivitiyConfigurationElement.Attribute("DefaultAction") != null)
            {
                DefaultInstanceName = AcivitiyConfigurationElement.Attribute("DefaultAction").Value;
                activity.DefaultAction = GetAction(WorkflowName, ActivityName, DefaultInstanceName);
            }
            activity.ActivityName = ActivityName;
            if (AcivitiyConfigurationElement.Attribute("NextActivity") != null)
                activity.NextActivity = AcivitiyConfigurationElement.Attribute("NextActivity").Value;
            if (AcivitiyConfigurationElement.Attribute("PreviousActivity") != null)
                activity.PreviousActivity = AcivitiyConfigurationElement.Attribute("PreviousActivity").Value;
            activity.State = AcivitiyConfigurationElement.Attribute("State").Value;

            return activity;
        }



        private static XElement GetWorkflowElement(string WorkflowName)
        {
            XDocument configurationDocument = LoadConfigurationDocument(WorkflowName);
            XElement workflowElement = configurationDocument
                                            .Elements("Workflow")
                                            .SingleOrDefault(p => p.Attribute("Name").Value == WorkflowName);
            if (workflowElement == null) throw CruxWorkflowExceptions.WorkflowConfigurationNotFoundException;
            return workflowElement;
        }

        //***************** Newly added**************************//
        public static List<Condition> GetConditions(string WorkflowName, string ActivityName, string ActionName)
        {
            XElement activityConfiguration = GetActivityElement(WorkflowName, ActivityName);
            XElement actionElement = activityConfiguration.Element("Actions")
                .Elements("Action")
                .SingleOrDefault(p => p.Attribute("Name").Value == ActionName);

            return
                actionElement.Elements("Conditions")
                .Descendants("Condition")
                .Select(c => new Condition
                {
                    ResultantActivity = c.Attribute("ResultantActivity") == null ? string.Empty : string.IsNullOrEmpty(c.Attribute("ResultantActivity").Value) ? string.Empty : c.Attribute("ResultantActivity").Value,
                    ActionClass = c.Attribute("ActionClass") == null ? string.Empty : string.IsNullOrEmpty(c.Attribute("ActionClass").Value) ? string.Empty : c.Attribute("ActionClass").Value,
                    SubConditions = c.Descendants("SubCondition").Select(sc => new SubCondition
                                   {
                                       AttributeName = sc.Attribute("AttributeName") == null ? string.Empty : string.IsNullOrEmpty(sc.Attribute("AttributeName").Value) ? string.Empty : sc.Attribute("AttributeName").Value,
                                       ConditionKey = sc.Attribute("ConditionKey") == null ? string.Empty : string.IsNullOrEmpty(sc.Attribute("ConditionKey").Value) ? string.Empty : sc.Attribute("ConditionKey").Value,
                                       DataType = sc.Attribute("DataType") == null ? string.Empty : string.IsNullOrEmpty(sc.Attribute("DataType").Value) ? string.Empty : sc.Attribute("DataType").Value,
                                       Value1 = sc.Attribute("Value1") == null ? string.Empty : string.IsNullOrEmpty(sc.Attribute("Value1").Value) ? string.Empty : sc.Attribute("Value1").Value,
                                       Value2 = sc.Attribute("Value2") == null ? string.Empty : string.IsNullOrEmpty(sc.Attribute("Value2").Value) ? string.Empty : sc.Attribute("Value2").Value,
                                   }).ToList()
                }).ToList();
        }
        /**********************For RoutingRule**********************/
        public static RoutingRule GetRoutingRuleForTheAction(string workflowName, string activityName, string actionName)
        {
            var action = GetAction(workflowName, activityName, actionName);
            RoutingRule routingRule = null;
            if (string.IsNullOrEmpty(action.RoutingRuleName))
                return new RoutingRule();

            var ruleElements = GetRoutingRuleElement(workflowName, action.RoutingRuleName);

            List<RoutingCondition> routingConditions = ruleElements.Elements("Condition")
                                   .Select(cond => new RoutingCondition
                                    {
                                        ConditionTye = cond.Attribute("Type").Value,
                                        Roles = (cond.Descendants("Roles").Elements("Role")
                                                .Select(a => a.Attribute("Name").Value)).ToList(),

                                        UserStates = (cond.Descendants("UserStates").Elements("State")
                                                .Select(a => new UserStateInfo
                                                {
                                                    StateName = a.Attribute("Name").Value,
                                                    Order = Convert.ToInt32(a.Attribute("Order").Value),
                                                    PickLastRequestInitiator = Convert.ToBoolean(a.Attribute("PickLastRequestInitiator").Value),
                                                })).ToList(),

                                        WorkflowStates = (cond.Descendants("WorkflowStates").Elements("State")
                                                        .Select(a => a.Attribute("Name").Value)).ToList(),
                                    }).ToList();

            List<RoutingCondition> overrideConditions = ruleElements.Element("Override")
                                   .Descendants("Condition")
                                   .Select(cond => new RoutingCondition
                                    {
                                        ConditionTye = cond.Attribute("Type").Value,
                                        Roles = (cond.Descendants("Roles").Elements("Role")
                                            .Select(a => a.Attribute("Name").Value)).ToList(),

                                        UserStates = (cond.Descendants("UserStates").Elements("State")
                                                .Select(a => new UserStateInfo
                                                {
                                                    StateName = a.Attribute("Name").Value,
                                                    Order = Convert.ToInt32(a.Attribute("Order").Value),
                                                    PickLastRequestInitiator = Convert.ToBoolean(a.Attribute("PickLastRequestInitiator").Value),
                                                })).ToList(),

                                        WorkflowStates = (cond.Descendants("WorkflowStates").Elements("State")
                                                        .Select(a => a.Attribute("Name").Value)).ToList(),
                                    }).ToList();

            routingRule = new RoutingRule
                          {
                              RuleName = action.RoutingRuleName,
                              RoutingConditions = routingConditions,
                              OverrideConditions = overrideConditions,
                          };

            return routingRule ?? new RoutingRule();
        }
        public static RoutingRule GetRoutingRuleForTheActivity(string workflowName, string activityName, string stateName)
        {
            var activity = GetActivityElement(workflowName, activityName);
            RoutingRule routingRule = null;

            if (activity.Attribute("State").Value.ToLower().Equals(stateName.ToLower()))
            {
                var routingRuleName = activity.Attribute("RoutingRule") == null ? string.Empty : string.IsNullOrEmpty(activity.Attribute("RoutingRule").Value) ? string.Empty : activity.Attribute("RoutingRule").Value;
                if (string.IsNullOrEmpty(routingRuleName))
                    return new RoutingRule();


                var ruleElements = GetRoutingRuleElement(workflowName, routingRuleName);

                List<RoutingCondition> routingConditions = ruleElements.Elements("Condition")
                                       .Select(cond => new RoutingCondition
                                        {
                                            ConditionTye = cond.Attribute("Type").Value,
                                            Roles = (cond.Descendants("Roles").Elements("Role")
                                                    .Select(a => a.Attribute("Name").Value)).ToList(),

                                            UserStates = (cond.Descendants("UserStates").Elements("State")
                                                .Select(a => new UserStateInfo
                                                {
                                                    StateName = a.Attribute("Name").Value,
                                                    Order = Convert.ToInt32(a.Attribute("Order").Value),
                                                    PickLastRequestInitiator = Convert.ToBoolean(a.Attribute("PickLastRequestInitiator").Value),
                                                })).ToList(),

                                            WorkflowStates = (cond.Descendants("WorkflowStates").Elements("State")
                                                        .Select(a => a.Attribute("Name").Value)).ToList(),

                                        }).ToList();

                List<RoutingCondition> overrideConditions = ruleElements.Element("Override")
                                       .Descendants("Condition")
                                       .Select(cond => new RoutingCondition
                                        {
                                            ConditionTye = cond.Attribute("Type").Value,
                                            Roles = (cond.Descendants("Roles").Elements("Role")
                                                .Select(a => a.Attribute("Name").Value)).ToList(),

                                            UserStates = (cond.Descendants("UserStates").Elements("State")
                                               .Select(a => new UserStateInfo
                                                    {
                                                        StateName = a.Attribute("Name").Value,
                                                        Order = Convert.ToInt32(a.Attribute("Order").Value),
                                                        PickLastRequestInitiator = Convert.ToBoolean(a.Attribute("PickLastRequestInitiator").Value),
                                                    })).ToList(),

                                            WorkflowStates = (cond.Descendants("WorkflowStates").Elements("State")
                                                    .Select(a => a.Attribute("Name").Value)).ToList(),
                                        }).ToList();

                routingRule = new RoutingRule
                              {
                                  RuleName = routingRuleName,
                                  RoutingConditions = routingConditions,
                                  OverrideConditions = overrideConditions,
                              };


            }
            return routingRule ?? new RoutingRule(); // if routingRule is null the return a new object of RoutingRule
        }
        private static XElement GetRoutingRuleElement(string WorkflowName, string routingRuleName)
        {
            XElement workflow = GetWorkflowElement(WorkflowName);
            XElement routingRuleConfiguration = workflow.Element("RoutingRules")
                                                     .Elements("Rule")
                                                     .SingleOrDefault(p => p.Attribute("Name").Value == routingRuleName);
            if (routingRuleConfiguration == null) throw CruxWorkflowExceptions.RuleNotFoundInWorkflowException;
            return routingRuleConfiguration;
        }
        /**********************End RoutingRule**********************/
    }
}
